package cn.xiongxf.user.service.impl;

import cn.xiongxf.basic.constant.VerifyCodeConstant;
import cn.xiongxf.basic.constant.WxConstants;
import cn.xiongxf.basic.exception.BusinessException;
import cn.xiongxf.basic.util.AjaxResult;
import cn.xiongxf.basic.util.HttpUtil;
import cn.xiongxf.basic.util.Md5Utils;
import cn.xiongxf.basic.util.StrUtils;
import cn.xiongxf.system.domain.Menu;
import cn.xiongxf.system.mapper.MenuMapper;
import cn.xiongxf.system.mapper.PermissionMapper;
import cn.xiongxf.user.domain.LoginDto;
import cn.xiongxf.user.domain.Logininfo;
import cn.xiongxf.user.domain.User;
import cn.xiongxf.user.domain.Wxuser;
import cn.xiongxf.user.jwt.JwtUtils;
import cn.xiongxf.user.jwt.LoginData;
import cn.xiongxf.user.jwt.RsaUtils;
import cn.xiongxf.user.mapper.LogininfoMapper;
import cn.xiongxf.user.mapper.UserMapper;
import cn.xiongxf.user.mapper.WxuserMapper;
import cn.xiongxf.user.service.ILogininfoService;
import cn.xiongxf.basic.service.impl.BaseServiceImpl;
import com.alibaba.fastjson.JSONObject;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.security.PrivateKey;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * 业务实现类：
 */
@Service
public class LogininfoServiceImpl extends BaseServiceImpl<Logininfo> implements ILogininfoService {
    @Autowired
    private LogininfoMapper logininfoMapper;
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private WxuserMapper wxuserMapper;
    @Autowired
    private UserMapper userMapper;
    @Autowired
    private PermissionMapper permissionMapper;
    @Autowired
    private MenuMapper menuMapper;

    @Override
    public AjaxResult accountLogin(LoginDto loginDto) {
        String account = loginDto.getAccount();
        String password = loginDto.getPassword();
        String type = loginDto.getType();
        // 1.校验
        // 1.1.空值校验
        if (StringUtils.isEmpty(account)
        || StringUtils.isEmpty(password)
        || StringUtils.isEmpty(type)){
            throw new BusinessException("登陆信息不能为空");
        }

        // 1.2.账号存在校验
        Logininfo logininfo = logininfoMapper.loadByAccount(loginDto);
        if (logininfo == null){
            throw new BusinessException("账号错误！");
        }
        // 1.3.密码正确校验
        String dbPwd = logininfo.getPassword();
        String salt = logininfo.getSalt();
        String loginPwd = Md5Utils.encrypByMd5(password + salt);
        if (!loginPwd.equals(dbPwd)){
            throw new BusinessException("密码错误！");
        }

        /*// 2.登陆成功将登陆信息保存在redis中（方便后续判断是否已登陆）
        String token = UUID.randomUUID().toString();
        // 这个数据要响应给前端,为了安全置空
        logininfo.setSalt(null);
        logininfo.setPassword(null);
        redisTemplate.opsForValue().set(token,logininfo,30, TimeUnit.MINUTES);

        // 3.响应信息，把token（后续判断登陆）和logininfo（显示用户名）响应到前端
        Map<String, Object> map = new HashMap<>();
        map.put("token",token);
        map.put("logininfo",logininfo);*/

        // redis换JWT
        Map<String,Object> map = loginSuccessJwtHandler(logininfo);

        // 响应数据
        return AjaxResult.me().setResultObj(map);
    }

    private Map<String, Object> loginSuccessJwtHandler(Logininfo logininfo) {
        try {
            Map<String, Object> map = new HashMap<>();
            LoginData loginData = new LoginData();
            // 一、获取4个数据（员工需要4个，用户只需1、4）
            // 1.登陆信息 - 参数中
            map.put("logininfo",logininfo);  // 在if外写，员工、用户（type = 0/1）都有
            loginData.setLogininfo(logininfo);
            if (logininfo.getType() == 0){
                // 2.当前登陆人的所有权限的sn集合 - 之前写过，直接调方法
                List<String> permissionSns = permissionMapper.loadPermissionSnsByLogininfoId(logininfo.getId());
                loginData.setPermissionSns(permissionSns);
                map.put("permissionSns",permissionSns);
                // 3.当前登陆人的所有二级菜单及其它的父级菜单（前端需要） - 难点
                List<Menu> menus = menuMapper.loadMenusByLogininfoId(logininfo.getId());
                loginData.setMenus(menus);
                map.put("menus",menus);
            }
            // 4.将以上3个数据封装到LoginData

            // 二、获取私钥，使用私钥对登陆信息加密，得到jwtToken串
            PrivateKey privateKey = RsaUtils.getPrivateKey(JwtUtils.class.getClassLoader().getResource("auth_rsa.pri").getFile());
            String jwsToken = JwtUtils.generateTokenExpireInMinutes(loginData, privateKey, 30000);  // 30，设长点
            map.put("token",jwsToken);

            // 三、将4个数据封装到map并返回。 疑问：jwtToken串中不是已经前3个数据了吗？- 前端没有解密
            return map;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    @Override
    public AjaxResult wechatLogin(String code) {
        // 1.获取发送第二个请求的参数
        String url = WxConstants.GET_ACK_URL
                .replace("APPID",WxConstants.APPID)
                .replace("SECRET",WxConstants.SECRET)
                .replace("CODE",code);

        // 2.发送请求 - 返回json格式的字符串数据
        String jsonStr = HttpUtil.httpGet(url);

        // 3.json字符串转json对象，获取access_token和openid
        JSONObject jsonObject = JSONObject.parseObject(jsonStr);
        String accessToken = jsonObject.getString("access_token");
        String openId = jsonObject.getString("openid");

        // 通过openId查询数据库
        Wxuser wxuser = wxuserMapper.loadBYOpenId(openId);
        // 4.1.查询到了，且user_id有值 = 以前绑定过 = 直接免密登录
        if (wxuser != null && wxuser.getUserId() != null
            && userMapper.loadById(wxuser.getUserId()) != null ){
            // 免密登录 = 不需要通过页面输入账号密码就直接登录：token，logininfo

            /*// 4.登录成功将登录信息保存在redis - 方便后续判断是否已登陆
            String token = UUID.randomUUID().toString();
            User user = userMapper.loadById(wxuser.getUserId());
            Logininfo logininfo = logininfoMapper.loadById(user.getLogininfoId());

            // logininfo会响应给前端，为了安全，置空盐值和密码
            logininfo.setSalt(null);
            logininfo.setPassword(null);
            redisTemplate.opsForValue().set(token,logininfo,30,TimeUnit.MINUTES);

            // 5.响应信息，并且将token【后续判断登录】和logininfo【显示用户名】响应给前端
            Map<String, Object> map = new HashMap<>();
            map.put("token",token);
            map.put("logininfo",logininfo);*/

            User user = userMapper.loadById(wxuser.getUserId());
            Logininfo logininfo = logininfoMapper.loadById(user.getLogininfoId());

            Map<String,Object> map = loginSuccessJwtHandler(logininfo);
            return AjaxResult.me().setResultObj(map);
        }else {
            // 4.2.没有查询到，响应给前端进行跳转 = 设置success=false，并且将token和openid传递给前端
            String param = "?accessToken="+accessToken+"&openId="+openId;
            return AjaxResult.me().setSuccess(false).setResultObj(param);
        }
    }

    @Override
    public AjaxResult wechatBinder(Map<String, String> map) {
        String phone = map.get("phone");
        String verifyCode = map.get("verifyCode");
        String accessToken = map.get("accessToken");
        String openId = map.get("openId");

        // 一、校验 - 空值校验
        if(StringUtils.isEmpty(phone) || StringUtils.isEmpty(verifyCode)){
            throw new BusinessException("信息不能为空！");
        }

        // 二、校验 - 手机验证码是否过期，是否正确 [业务键:phone 验证码:时间戳]
        Object obj = redisTemplate.opsForValue().get(VerifyCodeConstant.BINDER_CODE_PREFIX + phone);
        if(obj == null){
            throw new BusinessException("验证码已过期！");
        }
        // redis保存的验证码 和 用户输入的验证码  是否一致
        if(!obj.toString().split(":")[0].equals(verifyCode)){
            throw new BusinessException("验证码错误！");
        }

        // 三、通过accessToken和openid发送第三个请求
        String url = WxConstants.GET_USER_URL
                .replace("ACCESS_TOKEN", accessToken)
                .replace("OPENID", openId);
        String jsonStr = HttpUtil.httpGet(url);
        // 四、处理响应的数据：json字符串 - json对象 - 获取wxUser的信息
        Wxuser wxuser = jsonStr2Wxuser(jsonStr);

        // 五、绑定用户：将WxUser对象【第三个请求中获取】 与 User对象【通过手机创建】进行绑定
        User user = userMapper.loadByPhone(phone);
        if (user == null){
            // 根据电话没有找到user对象,通过phone创建User对象
            user = phone2User(phone);
            Logininfo logininfo = user2Logininfo(user);

            // 保存t_logininfo、t_user
            logininfoMapper.save(logininfo);
            user.setLogininfoId(logininfo.getId());
            userMapper.save(user);
        }

        // 至此，user、user_id都有了，保存t_wxuser
        wxuser.setUserId(user.getId());
        wxuserMapper.save(wxuser);

        /*// 做免密登录：token，logininfo对象
        String token = UUID.randomUUID().toString();
        Logininfo logininfo = logininfoMapper.loadById(user.getLogininfoId());
        // 安全起见，置空盐值和密码
        logininfo.setSalt(null);
        logininfo.setPassword(null);
        redisTemplate.opsForValue().set(token,logininfo,30, TimeUnit.MINUTES);

        // 响应信息，并且将token【后续判断登录】和logininfo【显示用户名】响应给前端
        Map<String,Object> mapTemp = new HashMap<>();
        mapTemp.put("token",token);
        mapTemp.put("logininfo",logininfo);*/

        Logininfo logininfo = logininfoMapper.loadById(user.getLogininfoId());

        Map<String,Object> mapTemp = loginSuccessJwtHandler(logininfo);

        return new AjaxResult().setResultObj(mapTemp);
    }

    // 自定义的对象转化方法
    private Logininfo user2Logininfo (User user) {
        Logininfo logininfo = new Logininfo();

        BeanUtils.copyProperties(user,logininfo);
        logininfo.setType(1);

        return logininfo;
    }

    private User phone2User (String phone) {
        User user = new User();

        user.setUsername(phone);
        user.setPhone(phone);
        String salt = StrUtils.getComplexRandomString(32);
        // 随机密码，实际上用不到
        String randomPwd = StrUtils.getComplexRandomString(6);
        String md5Pwd = Md5Utils.encrypByMd5(randomPwd + salt);
        user.setSalt(salt);
        user.setPassword(md5Pwd);
        user.setState(1);

        return user;
    }

    private Wxuser jsonStr2Wxuser (String jsonStr) {
        // 字符串转对象
        JSONObject jsonObject = JSONObject.parseObject(jsonStr);

        Wxuser wxuser = new Wxuser();

        wxuser.setOpenid(jsonObject.getString("openid"));
        wxuser.setNickname(jsonObject.getString("nickname"));
        wxuser.setSex(jsonObject.getInteger("sex"));
        wxuser.setHeadimgurl(jsonObject.getString("headimgurl"));
        wxuser.setUnionid(jsonObject.getString("unionid"));

        // 设置地址
        String country = jsonObject.getString("country");
        String province = jsonObject.getString("province");
        String city = jsonObject.getString("city");
        wxuser.setAddress(country + " " + province + " " + city);

        return wxuser;
    }

}
